package com.zxy.allSearcher.datasource;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zxy.allSearcher.common.ErrorCode;
import com.zxy.allSearcher.exception.BusinessException;
import com.zxy.allSearcher.model.dto.post.PostQueryRequest;
import com.zxy.allSearcher.model.dto.user.UserQueryRequest;
import com.zxy.allSearcher.model.entity.Picture;
import com.zxy.allSearcher.model.enums.SearchTypeEnum;
import com.zxy.allSearcher.model.vo.PostVO;
import com.zxy.allSearcher.model.vo.SearchVO;
import com.zxy.allSearcher.model.vo.UserVO;
import com.zxy.allSearcher.service.PostService;
import com.zxy.allSearcher.service.UserService;
import com.zxy.allSearcher.utils.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * 数据源接口
 *
 * @author zxy
 * @since 2023/9/16
 **/
public interface DataSource<T> {
    /**
     * 数据分页查询
     *
     * @param searchText 检索词
     * @param current 序号
     * @param size 数量
     */
    Page<T> doSearch(String searchText, long current, long size);
}

/**
 * 用户数据源
 */
@Service
@Slf4j
class UserDataSource implements DataSource<UserVO> {
    @Resource
    private UserService userService;
    @Override
    public Page<UserVO> doSearch(String searchText, long current, long size) {
        UserQueryRequest userQueryRequest = new UserQueryRequest();

        userQueryRequest.setCurrent(current);
        userQueryRequest.setPageSize(size);
        userQueryRequest.setUserName(searchText);

        return userService.listUserVOByPage(userQueryRequest);
    }
}

/**
 * 帖子数据源
 */
@Service
@Slf4j
class PostDataSource implements DataSource<PostVO> {

    @Resource
    private PostService postService;

    /**
     * 有些时候会遇到这种完全无法适配的情况
     * 方法：1、与被适配者服务的提供者沟通，提供适配接口
     */
    @Override
    public Page<PostVO> doSearch(String searchText, long current, long size) {
        PostQueryRequest postQueryRequest = new PostQueryRequest();
        postQueryRequest.setSearchText(searchText);
        postQueryRequest.setCurrent(current);
        postQueryRequest.setPageSize(size);

        // 获取请求
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        return postService.listPostVOByPage(postQueryRequest, request);
    }
}

/**
 * 图片数据源
 */
@Service
@Slf4j
class PictureDataSource implements DataSource<Picture> {

    @Override
    public Page<Picture> doSearch(String searchText, long current, long size) {
        // 计算当前页面
        long pageNum = (current - 1) * size;
        // 1、数据获取
        String url = String.format("https://www.bing.com/images/search?q=%s&first=%s", searchText, pageNum);
        Document doc;
        try {
            doc = Jsoup.connect(url).get();
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "数据获取异常");
        }
        Elements elements = doc.select(".iuscp.isv");
        // 2、数据转换，注意有些图片数据是压缩，懒加载，缩略图的形式，是无法获取完整的图源信息的（得爬仔细）
        List<Picture> pictures = new ArrayList<>();
        // 深度爬取
        for (Element element : elements) {
            // 图片url
            String src = element.select(".mimg").get(0).attr("src");
            System.out.println(src);
            // 标题
            String title = element.select(".inflnk").get(0).attr("aria-label");
            System.out.println(title);
            // 数据封装
            Picture picture = new Picture();
            picture.setUrl(src);
            picture.setTitle(title);
            pictures.add(picture);

            // 根据分页大小，终止转换
            // 感觉在爬取时就应该约定好，还可以提升性能+一致性
            if (pictures.size() > size) {
                break;
            }
        }
        // 3、分页展示，由于无法估计爬出的数据总数，无法返回
        Page<Picture> picturePage = new Page<>(current, size);
        picturePage.setRecords(pictures);
        return picturePage;
    }
}
